ButterworthLPF   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 108
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 55
dl 0
loc 108
rs 10
c 0
b 0
f 0
wmc 10

6 Functions

Rating   Name   Duplication   Size   Complexity  
A constructor 0 23 3
A filter 0 8 2
A preCalc_ 0 14 1
A runStage_ 0 11 1
A reset 0 5 2
A getCoeffs_ 0 13 1
1
/*
2
 * Copyright (c) 2019 Rafael da Silva Rocha.
3
 * Copyright (c) 2014 Florian Markert
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining
6
 * a copy of this software and associated documentation files (the
7
 * "Software"), to deal in the Software without restriction, including
8
 * without limitation the rights to use, copy, modify, merge, publish,
9
 * distribute, sublicense, and/or sell copies of the Software, and to
10
 * permit persons to whom the Software is furnished to do so, subject to
11
 * the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be
14
 * included in all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
 *
24
 */
25
26
/**
27
 * @fileoverview Butterworth LPF. Based on the Butterworth LPF from Fili.js.
28
 * @see https://github.com/rochars/wavefile
29
 * @see https://github.com/markert/fili.js
30
 */
31
32
/**
33
 * Butterworth LPF.
34
 */
35
export class ButterworthLPF {
36
  
37
  /**
38
   * @param {number} order The order of the filter.
39
   * @param {number} sampleRate The sample rate.
40
   * @param {number} cutOff The cut off frequency.
41
   */
42
  constructor(order, sampleRate, cutOff) {
43
    /** @type {!Array} */
44
    let filters = [];
45
    for (let i = 0; i < order; i++) {
46
      filters.push(this.getCoeffs_({
47
        Fs: sampleRate,
48
        Fc: cutOff,
49
        Q: 0.5 / (Math.sin((Math.PI / (order * 2)) * (i + 0.5)))
50
      }));
51
    }
52
    this.stages = [];
53
    for (let i = 0; i < filters.length; i++) {
54
      this.stages[i] = {
55
        b0 : filters[i].b[0],
56
        b1 : filters[i].b[1],
57
        b2 : filters[i].b[2],
58
        a1 : filters[i].a[0],
59
        a2 : filters[i].a[1],
60
        k : filters[i].k,
61
        z : [0, 0]
62
      };
63
    }
64
  }
65
66
  /**
67
   * @param {number} sample A sample of a sequence.
68
   * @return {number}
69
   */
70
  filter(sample) {
71
    /** @type {number} */
72
    let out = sample;
73
    for (let i = 0, len = this.stages.length; i < len; i++) {
74
      out = this.runStage_(i, out);
75
    }
76
    return out;
77
  }
78
79
  /**
80
   * @param {!Object} params The filter params.
81
   * @return {!Object}
82
   */
83
  getCoeffs_(params) {
84
    /** @type {!Object} */
85
    let coeffs = {};
86
    coeffs.a = [];
87
    coeffs.b = [];
88
    /** @type {!Object} */
89
    let p = this.preCalc_(params, coeffs);
90
    coeffs.k = 1;
91
    coeffs.b.push((1 - p.cw) / (2 * p.a0));
92
    coeffs.b.push(2 * coeffs.b[0]);
93
    coeffs.b.push(coeffs.b[0]);
94
    return coeffs;
95
  }
96
97
  /**
98
   * @param {!Object} params The filter params.
99
   * @param {!Object} coeffs The coefficients template.
100
   * @return {!Object}
101
   */
102
  preCalc_(params, coeffs) {
103
    /** @type {!Object} */
104
    let pre = {};
105
    /** @type {number} */
106
    let w = 2 * Math.PI * params.Fc / params.Fs;
107
    pre.alpha = Math.sin(w) / (2 * params.Q);
108
    pre.cw = Math.cos(w);
109
    pre.a0 = 1 + pre.alpha;
110
    coeffs.a0 = pre.a0;
111
    coeffs.a.push((-2 * pre.cw) / pre.a0);
112
    coeffs.k = 1;
113
    coeffs.a.push((1 - pre.alpha) / pre.a0);
114
    return pre;
115
  }
116
  
117
  /**
118
   * @param {number} i The stage index.
119
   * @param {number} sample The sample.
120
   * @return {number}
121
   */
122
  runStage_(i, sample) {
123
    /** @type {number} */
124
    let temp = sample * this.stages[i].k - this.stages[i].a1 *
125
      this.stages[i].z[0] - this.stages[i].a2 * this.stages[i].z[1];
126
    /** @type {number} */
127
    let out = this.stages[i].b0 * temp + this.stages[i].b1 *
128
      this.stages[i].z[0] + this.stages[i].b2 * this.stages[i].z[1];
129
    this.stages[i].z[1] = this.stages[i].z[0];
130
    this.stages[i].z[0] = temp;
131
    return out;
132
  }
133
134
  /**
135
   * Reset the filter.
136
   */
137
  reset() {
138
    for (let i = 0; i < this.stages.length; i++) {
139
      this.stages[i].z = [0, 0];
140
    }
141
  }
142
}
143